#include <buola/gui/x11/auto.h>

#include <buola/gui/x11/cx11sync.h>
#include <buola/gui/x11/cx11display.h>

namespace buola { namespace gui { namespace x11 {

bool CX11SyncCounter::mInitStatic=false;
int CX11SyncCounter::mEvent=0;
void *CX11SyncCounter::mSystemCounters=nullptr;
std::vector<std::wstring> CX11SyncCounter::mSystemCounterList;

std::vector<CX11SyncAlarm*> CX11SyncAlarm::mAll;

CX11SyncCounter::CX11SyncCounter(int64_t pValue)
{
    if(!mInitStatic)
    {
        int lDummy,lDummy2;
        XSyncQueryExtension(get_default_x11_display()->GetDisplay(),&mEvent,&lDummy);
        XSyncInitialize(get_default_x11_display()->GetDisplay(),&lDummy,&lDummy2);
        mInitStatic=true;
    }

    XSyncValue lValue;
    XSyncIntsToValue(&lValue,pValue&0xffffffff,pValue>>32);
    mID=XSyncCreateCounter(get_default_x11_display()->GetDisplay(),lValue);
    mOwned=true;
}

CX11SyncCounter::CX11SyncCounter(TID pID,bool pOwn)
{
    if(!mInitStatic)
    {
        int lDummy,lDummy2;
        XSyncQueryExtension(get_default_x11_display()->GetDisplay(),&mEvent,&lDummy);
        XSyncInitialize(get_default_x11_display()->GetDisplay(),&lDummy,&lDummy2);
        mInitStatic=true;
    }

    mID=pID;
    mOwned=pOwn;
}

CX11SyncCounter::CX11SyncCounter(const std::wstring& /*pName*/)
{
    GetSystemCounterList();

    mOwned=false;
}

CX11SyncCounter::~CX11SyncCounter()
{
    if(mOwned)
        XSyncDestroyCounter(get_default_x11_display()->GetDisplay(),mID);
}

void CX11SyncCounter::Set(int64_t pValue)
{
    XSyncValue lValue;
    XSyncIntsToValue(&lValue,pValue&0xffffffff,pValue>>32);

    XSyncSetCounter(get_default_x11_display()->GetDisplay(),mID,lValue);
}

void CX11SyncCounter::Add(int64_t pValue)
{
    XSyncValue lValue;
    XSyncIntsToValue(&lValue,pValue&0xffffffff,pValue>>32);

    XSyncChangeCounter(get_default_x11_display()->GetDisplay(),mID,lValue);
}

int64_t CX11SyncCounter::Get()
{
    XSyncValue lValue;
    XSyncQueryCounter(get_default_x11_display()->GetDisplay(),mID,&lValue);

    return (((int64_t)XSyncValueLow32(lValue))|(((int64_t)XSyncValueHigh32(lValue))<<32));
}

void CX11SyncCounter::Await(int64_t pValue,EWaitType pFlags,int /*pTimeout*/)
{
    XSyncWaitCondition lCondition;
    lCondition.trigger.counter=mID;

    if(pFlags&SYNC_RELATIVE)
        lCondition.trigger.value_type=XSyncRelative;
    else
        lCondition.trigger.value_type=XSyncAbsolute;

    XSyncIntsToValue(&lCondition.trigger.wait_value,
                        pValue&0xffffffff,pValue>>32);

    switch((int)(pFlags&(SYNC_NEGATIVE|SYNC_TRANSITION)))
    {
    case SYNC_NONE:
        lCondition.trigger.test_type=XSyncPositiveComparison;
        break;
    case SYNC_NEGATIVE:
        lCondition.trigger.test_type=XSyncNegativeComparison;
        break;
    case SYNC_TRANSITION:
        lCondition.trigger.test_type=XSyncPositiveTransition;
        break;
    case (int)SYNC_NEGATIVE|(int)SYNC_TRANSITION:
        lCondition.trigger.test_type=XSyncNegativeTransition;
        break;
    }

    XSyncIntToValue(&lCondition.event_threshold,0);

    XSyncAwait(get_default_x11_display()->GetDisplay(),&lCondition,1);
}

const std::vector<std::wstring> &CX11SyncCounter::GetSystemCounterList()
{
    if(!mInitStatic)
    {
        int lDummy,lDummy2;
        XSyncQueryExtension(get_default_x11_display()->GetDisplay(),&mEvent,&lDummy);
        XSyncInitialize(get_default_x11_display()->GetDisplay(),&lDummy,&lDummy2);
        mInitStatic=true;
    }

    if(!mSystemCounters)
    {
        int lCount;
        XSyncSystemCounter *lCounters;
        lCounters=XSyncListSystemCounters(
        get_default_x11_display()->GetDisplay(),&lCount);
        for(int i=0;i<lCount;i++)
        {
            mSystemCounterList.push_back(decode(lCounters[i].name,COD_ASCII));
        }
        mSystemCounters=lCounters;
    }

    return mSystemCounterList;
}

CX11SyncAlarm::CX11SyncAlarm(const CX11SyncCounter &pCounter,int64_t pValue,
                             CX11SyncCounter::EWaitType pFlags,int64_t pDelta)
    :mCounter(pCounter.GetID(),false)
{
    XSyncAlarmAttributes lAttrs;
    lAttrs.trigger.counter=pCounter.GetID();

    if(pFlags&CX11SyncCounter::SYNC_RELATIVE)
        lAttrs.trigger.value_type=XSyncRelative;
    else
        lAttrs.trigger.value_type=XSyncAbsolute;

    XSyncIntsToValue(&lAttrs.trigger.wait_value,
                        pValue&0xffffffff,pValue>>32);

    switch((int)(pFlags&(CX11SyncCounter::SYNC_NEGATIVE|CX11SyncCounter::SYNC_TRANSITION)))
    {
    case CX11SyncCounter::SYNC_NONE:
        lAttrs.trigger.test_type=XSyncPositiveComparison;
        break;
    case CX11SyncCounter::SYNC_NEGATIVE:
        lAttrs.trigger.test_type=XSyncNegativeComparison;
        break;
    case CX11SyncCounter::SYNC_TRANSITION:
        lAttrs.trigger.test_type=XSyncPositiveTransition;
        break;
    case (int)CX11SyncCounter::SYNC_NEGATIVE|(int)CX11SyncCounter::SYNC_TRANSITION:
        lAttrs.trigger.test_type=XSyncNegativeTransition;
        break;
    }

    XSyncIntsToValue(&lAttrs.delta,pDelta&0xffffffff,pDelta>>32);
    lAttrs.events=True;

    mID=XSyncCreateAlarm(get_default_x11_display()->GetDisplay(),
            XSyncCACounter|XSyncCAValueType|XSyncCAValue|
            XSyncCATestType|XSyncCADelta|XSyncCAEvents,&lAttrs);

    mDelete=(pFlags&CX11SyncCounter::SYNC_DELETE);

    mAll.push_back(this);

    XSyncQueryAlarm(get_default_x11_display()->GetDisplay(),mID,&lAttrs);
}

CX11SyncAlarm::~CX11SyncAlarm()
{
    XSyncDestroyAlarm(get_default_x11_display()->GetDisplay(),mID);

    mAll.erase(std::find(mAll.begin(),mAll.end(),this));
}

void CX11SyncAlarm::Trigger()
{
    sTriggered();

    if(mDelete)
        delete this;
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }
